import Stomp from "stompjs";
import _ from "lodash-es";
import { createGuid } from "@@/utils/tools";
import {
  IStompClient,
  IStompSubscribe,
  IStompHeaders,
  IStompFrame,
} from "@@/typings/stomp";

export default class CustomStomp {
  readonly clients: { [key: string]: IStompClient };
  readonly subscribeList: {
    [clientId: string]: {
      [key: string]: IStompSubscribe;
    };
  };

  constructor() {
    this.clients = this.init();
    this.subscribeList = {};
  }

  // 初始化
  init() {
    if (typeof WebSocket == "undefined") {
      console.error("不支持websocket");
    }
    return {};
  }

  /**
   * @descripttion: 创建客户端
   * @param {string} url 地址
   * @param {string} id 唯一id
   */
  createClient(url: string, id?: string): IStompClient | null {
    const guid = id || createGuid();

    /// 已存在
    if (this.clients[guid]) return null;

    const client = Stomp.client(url);
    client.debug = null;

    this.clients[guid] = client;
    this.subscribeList[guid] = {};
    return client;
  }

  /**
   * @descripttion: 连接
   * @param {IStompClient | string} client client|id
   * @param {IStompHeaders} headers 请求头
   */
  connect(
    client: IStompClient | string,
    headers: IStompHeaders,
    connectCallback?: (frame: IStompFrame) => void,
    errorCallback?: (frame: IStompFrame) => void
  ) {

    const onConnected = (frame: IStompFrame) => {
      connectCallback && connectCallback(frame);
    };
    const onFailed = (frame: IStompFrame) => {
      errorCallback && errorCallback(frame);
    };

    if (typeof client == "string")
      this.clients[client]?.connect(headers, onConnected, onFailed);
    else client.connect(headers, onConnected, onFailed);
  }

  /**
   * @descripttion: 订阅
   */
  subscribe(
    client: IStompClient | string,
    id: string,
    destination: string,
    callback: (frame: IStompFrame) => void,
  ): IStompSubscribe | null {
    const c = typeof client == "string" ? this.clients[client] : client,
    clientId = this.getIdByClient(c);
    /// 不存在客户端
    if (!clientId) return null;
    let subscribe = this.subscribeList[clientId][id];

    /// 已存在
    if (subscribe) return null;

    const onCallback = (frame) => {
      callback(frame);
    };

    subscribe = c.subscribe(destination, onCallback, { id });
    this.subscribeList[clientId][id] = subscribe;
    return subscribe;
  }

  /**
   * @descripttion: 发送消息
   */
  send(
    client: IStompClient | string,
    destination: string,
    body: string,
    headers?: IStompHeaders
  ) {
    if (typeof client == "string")
      this.clients[client]?.send(destination, headers, body);
    else client?.send(destination, headers, body);
  }

  /**
   * @descripttion: 取消订阅
   * @param {string} id 订阅id
   */
  unsubscribe(client: IStompClient | string, id: string) {
    const c = typeof client == "string" ? this.clients[client] : client,
      clientId = this.getIdByClient(c);

    if (!clientId) return false;

    const subscribe = this.subscribeList[clientId][id];

    // 不存在
    if (!subscribe) return false;

    subscribe.unsubscribe();
    delete this.subscribeList[clientId][id];
    return true;
  }

  /**
   * @descripttion: 断开连接，同时删除stomp客户端
   * @param {IStompClient | string} client 客户端
   * @param {function} callback 回调
   */
  disconnect(client: IStompClient | string, callback?: () => void) {
    if (typeof client == "string")
      this.clients[client]?.disconnect(() => {
        delete this.clients[client];
        callback && callback();
      });
    else {
      client?.disconnect(() => {
        for (const key in this.clients) {
          if (Object.prototype.hasOwnProperty.call(this.clients, key)) {
            const c = this.clients[key];
            _.isEqual(c, client) && delete this.clients[key];
          }
        }
        callback && callback();
      });
    }
  }

  /**
   * @descripttion: 删除客户端
   * @param {IStompClient} clinet
   */
  removeClient(
    client: IStompClient | string,
    callback?: (res?: Boolean) => void
  ) {
    if (typeof client == "string") delete this.clients[client];
    else
      for (const key in this.clients) {
        if (Object.prototype.hasOwnProperty.call(this.clients, key)) {
          const c = this.clients[key];
          _.isEqual(c, client) && delete this.clients[key];
        }
      }
    callback && callback(true);
  }

  /**
   * @descripttion: 根据客户端获取id
   * @param {IStompClient} client
   */
  private getIdByClient(client: IStompClient) {
    for (const key in this.clients) {
      if (Object.prototype.hasOwnProperty.call(this.clients, key)) {
        const element = this.clients[key];
        if (_.isEqual(client, element)) return key;
      }
    }
  }

  /**
   * @descripttion: 根据id获取客户端
   * @param {IStompClient} client
   * @return {*}
   */
  public getClientById(id: string) {
    return this.clients[id];
  }
}

function createStomp() {
  return new CustomStomp();
}

export const customStomp = createStomp();
